package gate

import (
	"CaptchaCluster/client/base"
	"CaptchaCluster/client/conf"
	"CaptchaCluster/defineMsg"
	"CaptchaCluster/log"
	"CaptchaCluster/msg"
	"context"
	"github.com/name5566/leaf/chanrpc"
	"github.com/name5566/leaf/network"
	"github.com/name5566/leaf/network/json"
	"net"
	"reflect"
	"sync"
	"time"
)

var client *TCPClient
var skeleton = base.NewSkeleton()

type TCPClient struct {
	client       *network.TCPClient
	AgentChanRPC *chanrpc.Server
}

func Init(gameRPC *chanrpc.Server) {
	c := TCPClient{
		client: &network.TCPClient{
			Mutex:           sync.Mutex{},
			Addr:            conf.Instance.ServerAddr,
			ConnNum:         1,
			ConnectInterval: 3 * time.Second,
			PendingWriteNum: 2000,
			AutoReconnect:   true,
			NewAgent: func(conn *network.TCPConn) network.Agent {
				return &Agent{
					conn:      conn,
					processor: msg.Processor,
				}
			},
			LenMsgLen:    2,
			MinMsgLen:    0,
			MaxMsgLen:    4096,
			LittleEndian: false,
		},
		AgentChanRPC: gameRPC,
	}
	client = &c
	client.client.Start()
}

// =========================================================================================

type Agent struct {
	conn      *network.TCPConn
	processor *json.Processor
}

func (a *Agent) Run() {
	client.AgentChanRPC.Go("NewAgent", a)

	ctx, cancel := context.WithCancel(context.TODO())
	go a.pong(ctx)

	for {
		data, err := a.conn.ReadMsg()
		if err != nil {
			log.Debug("read message: %v", err)
			break
		}

		if a.processor != nil {
			msg, err := a.processor.Unmarshal(data)
			if err != nil {
				log.Debug("unmarshal message error: %v", err)
				break
			}

			err = a.processor.Route(msg, a)
			if err != nil {
				log.Debug("route message error: %v", err)
				break
			}
		}
	}
	cancel()
}

func (a *Agent) OnClose() {
	log.Release("disconnect from %s , will retry the connection", conf.Instance.ServerAddr)
	client.AgentChanRPC.Call0("CloseAgent", a)
}

func (a *Agent) WriteMsg(msg interface{}) {
	if a.processor != nil {
		data, err := a.processor.Marshal(msg)
		if err != nil {
			log.Error("marshal message %v error: %v", reflect.TypeOf(msg), err)
			return
		}
		err = a.conn.WriteMsg(data...)
		if err != nil {
			log.Error("write message %v error: %v", reflect.TypeOf(msg), err)
		}
	}
}

func (a *Agent) LocalAddr() net.Addr {
	return a.conn.LocalAddr()
}

func (a *Agent) RemoteAddr() net.Addr {
	return a.conn.RemoteAddr()
}

func (a *Agent) Close() {
	a.conn.Close()
}

func (a *Agent) Destroy() {
	a.conn.Destroy()
}

func (a *Agent) UserData() interface{} {
	return nil
}

func (a *Agent) SetUserData(data interface{}) {

}

func (a *Agent) pong(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			time.Sleep(1 * time.Second)
			a.WriteMsg(&defineMsg.ReqPong{})
		}
	}
}
